package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.ServiceCapability;
import com.huaweicloud.sdk.iotda.v5.model.ServiceProperty;
import com.huaweicloud.sdk.iotda.v5.model.ShowProductRequest;
import com.huaweicloud.sdk.iotda.v5.model.ShowProductResponse;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.userThreadLocal.UserThreadLocal;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.framework.web.service.TokenService;
import com.zzyl.nursing.domain.*;
import com.zzyl.nursing.dto.DayDataDto;
import com.zzyl.nursing.dto.UserLoginRequestDto;
import com.zzyl.nursing.dto.member.FamilyMemberDto;
import com.zzyl.nursing.dto.member.HealthDto;
import com.zzyl.nursing.mapper.*;
import com.zzyl.nursing.service.FamilyMemberElderService;
import com.zzyl.nursing.service.FamilyMemberService;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.service.WechatService;
import com.zzyl.nursing.vo.*;
import org.apache.commons.lang3.math.NumberUtils;
import org.aspectj.weaver.ast.Var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class FamilyMemberServiceImpl extends ServiceImpl<FamilyMemberMapper, FamilyMember> implements FamilyMemberService {
    @Autowired
    private WechatService wechatService;
    @Autowired
    private TokenService tokenService;
    static List<String> DEFAULT_NICKNAME_PREFIX = ListUtil.of("生活更美好",
            "大桔大利",
            "日富一日",
            "好柿开花",
            "柿柿如意",
            "一椰暴富",
            "大柚所为",
            "杨梅吐气",
            "天生荔枝"
    );

    @Override
    public LoginVo login(UserLoginRequestDto userLoginRequestDto) {
        //1.先查family_member表中是否有对应openid的用户
        String openid = wechatService.getOpenid(userLoginRequestDto.getCode());
        if (openid == null) {
            new RuntimeException("微信用户不存在，登录失败");//openid是微信用户的唯一标识
        }
        FamilyMember familyMember = this.getOne(Wrappers.<FamilyMember>lambdaQuery().eq(FamilyMember::getOpenId, openid));
        if (familyMember == null) {
            familyMember = new FamilyMember();
        }
        String phone = wechatService.getPhone(userLoginRequestDto.getPhoneCode());
        //更新或修改操作
        saveOrUpdateFamilyMember(familyMember, openid, phone);
        //2.生成自定义token返回
        LoginVo loginVo = new LoginVo();
        Map<String, Object> map = new HashMap<>();
        map.put("userId", familyMember.getId());
        map.put("nickName", familyMember.getName());
        String token = tokenService.createToken(map);
        loginVo.setToken(token);
        loginVo.setNickName(familyMember.getName());
        return loginVo;
    }


    private void saveOrUpdateFamilyMember(FamilyMember familyMember, String openid, String phone) {
        //获取的手机号与数据库的手机号不一致
        if (ObjectUtil.notEqual(phone, familyMember.getPhone())) {
            familyMember.setPhone(phone);
        }
        if (familyMember.getId() == null) {
            //表示新增，openid，name
            String nickName = DEFAULT_NICKNAME_PREFIX.get((int) (Math.random() * DEFAULT_NICKNAME_PREFIX.size()))
                    + StringUtils.substring(familyMember.getPhone(), 7);
            familyMember.setOpenId(openid);
            familyMember.setName(nickName);
            this.save(familyMember);
            return;
        }
        //修改
        this.updateById(familyMember);
    }

    @Autowired
    private DeviceDataMapper deviceDataMapper;
    @Autowired
    private IDeviceDataService deviceDataService;

    /**
     * 天气按照时间查询
     * @param
     * @return
     */
    @Override
    public List<DayDataVo> queryDeviceDataListByDay(String functionId, Long startTimeStr, Long endTimeStr, String iotId) {
        /*
        思路分析
        根据前端传进来的dto中的数据进行条件查询
         */
        //将毫秒值转换成时间
        LocalDateTime startTime = DateUtil.date(startTimeStr).toLocalDateTime();
        LocalDateTime endTime = DateUtil.date(endTimeStr).toLocalDateTime();
        //调用DeviceDataService层进行条件查询
        List<DeviceData> list = deviceDataService.list(Wrappers.<DeviceData>lambdaQuery()
                .eq(DeviceData::getFunctionId, functionId)
                .eq(DeviceData::getIotId, iotId)
                .between(DeviceData::getAlarmTime, startTime, endTime));
        //遍历集合封装返回参数
        //将LocalDateTime的时间格式转换成"HH:mm"
        List<DayDataVo> targetList = new ArrayList<>();
        //根据每小时对数据进行分组
        Map<Integer,List<DeviceData>> deviceDataByHour = list.stream().collect(Collectors.groupingBy(item->item.getAlarmTime().getHour()));
        //获取当前时间
        Integer currentTimeHour = startTime.getHour();
        Integer endTimeHour = endTime.getHour();
        //进行循环
        while (currentTimeHour<=endTimeHour){
            if (deviceDataByHour.containsKey(currentTimeHour)) {
                //当前小时内有数据
                List<DeviceData> deviceDataList = deviceDataByHour.get(currentTimeHour);
                //遍历deviceDataList集合对数据求平均值
                double averageValue = deviceDataList.stream().mapToDouble(deviceData -> NumberUtils.toDouble(deviceData.getDataValue())).average().orElse(0.0);
                //封装数据
                DayDataVo deviceDataByTimeVo = DayDataVo.builder()
                        .dateTime(currentTimeHour.toString())
                        .dataValue(averageValue)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }else{
                //当前小时没有数据
                //封装数据
                DayDataVo deviceDataByTimeVo = DayDataVo.builder()
                        .dateTime(currentTimeHour.toString())
                        .dataValue(null)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }
            //步增语句
            currentTimeHour = currentTimeHour + 1;
        }
        return targetList;
    }


    @Autowired
    private FamilyMemberElderMapper familyMemberElderMapper;
    @Autowired
    private FamilyMemberMapper familyMemberMapper;
    @Autowired
    private ElderMapper elderMapper;

    /**
     * 绑定老人
     *
     * @param dto
     */
    @Override
    public void bind(FamilyMemberDto dto) {

        //通过身份证号和姓名查询老人表数据是否存在
        Elder elder = elderMapper.selectOne(Wrappers.<Elder>lambdaQuery()
                .eq(Elder::getIdCardNo, dto.getIdCard())
                .eq(Elder::getName, dto.getName())
                .eq(Elder::getStatus, 1)
        );
        if (ObjectUtil.isEmpty(elder)) {
            new RuntimeException("没有该老人");
        }
        //通过id查询当前登录用户的family_member表数据
        FamilyMember member = familyMemberMapper.selectOne(Wrappers.<FamilyMember>lambdaQuery()
                .eq(FamilyMember::getId, UserThreadLocal.getUserId())
        );
        //表示老人存在，进行向中间表的添加数据
        FamilyMemberElder familyMemberElder = new FamilyMemberElder();
        familyMemberElder.setFamilyMemberId(member.getId());
        familyMemberElder.setElderId(elder.getId());
        familyMemberElder.setRemark(dto.getRemark()); // 存储称呼关系
        //添加到家属—老人表中
        familyMemberElderMapper.insert(familyMemberElder);

    }

    @Autowired
    private FamilyMemberElderService familyMemberElderService;
    @Autowired
    private BedMapper bedMapper;
    @Autowired
    private RoomMapper roomMapper;
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 查询当前登录用户的所有家属
     *
     * @return
     */
    @Override
    public List<FamilyMemberVo> selectMyMember() {
        //查询所有
        List<FamilyMemberElder> list = familyMemberElderService.list();
        //封装成vo
        List<FamilyMemberVo> familyMemberVos = new ArrayList<>();
        list.forEach(FamilyMemberElder -> {
            FamilyMemberVo familyMemberVo = new FamilyMemberVo();
            familyMemberVo.setElderId(FamilyMemberElder.getElderId());
            familyMemberVo.setFamilyMemberId(FamilyMemberElder.getFamilyMemberId());
            familyMemberVo.setElderName(elderMapper.selectById(FamilyMemberElder.getElderId()).getName());
            familyMemberVo.setId(FamilyMemberElder.getId());
            familyMemberVos.add(familyMemberVo);
        });
        return familyMemberVos;
    }

    /**
     * 分页查询
     * @return
     */
    @Override
    public List<FamilyMemberElderPageVo> listByPage() {
        List<FamilyMemberElderPageVo> volist = new ArrayList<>();
        //查询客户与老人表
        List<FamilyMemberElder> list = familyMemberElderService.list();
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        list.forEach(familyMemberElder -> {
            FamilyMemberElderPageVo vo = new FamilyMemberElderPageVo();
            vo.setMid(String.valueOf(familyMemberElder.getId()));
            vo.setMremark(familyMemberElder.getRemark());
            vo.setElderId(String.valueOf(familyMemberElder.getElderId()));
            //根据 客户与老人表 中的老人id查询老人表
            Elder elder = elderMapper.selectById(familyMemberElder.getElderId());
            if (ObjectUtil.isEmpty(elder)) {
                return;
            }
            vo.setName(elder.getName());
            vo.setBedNumber(elder.getBedNumber());
            //根据老人表->bed表->room表
            Long bedId = elder.getBedId();
            Bed bed = bedMapper.selectById(bedId);
            if (ObjectUtil.isEmpty(bed)) {
                return;
            }
            Room room = roomMapper.selectOne(Wrappers.<Room>lambdaQuery().eq(Room::getId, bed.getRoomId()));
            if (ObjectUtil.isEmpty(room)) {
                return;
            }
            vo.setTypeName(room.getTypeName());
            Device device = deviceMapper.selectOne(Wrappers.<Device>lambdaQuery().eq(Device::getBindingLocation, elder.getId()));
            if (ObjectUtil.isEmpty(device)) {
                vo.setIotId(null);
                vo.setDeviceName(null);
                vo.setProductKey(null);
            } else {
                vo.setIotId(device.getIotId());
                vo.setDeviceName(device.getDeviceName());
                vo.setProductKey(device.getProductKey());
            }
            volist.add(vo);
        });

        return volist;
    }

    @Autowired
    private IoTDAClient iotClient;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public List<PropertyStatusInfoVo> QueryDevicePropertyStatus(HealthDto healthDto) {
        //从官网获取数据
        Device device = deviceMapper.selectOne(Wrappers.<Device>lambdaQuery()
                .eq(Device::getProductKey, healthDto.getProductKey()));
        if (ObjectUtil.isEmpty(device)){
            throw new BaseException("设备不存在");
        }
        ShowProductRequest request = new ShowProductRequest();
        request.setProductId(healthDto.getProductKey());
        ShowProductResponse response = iotClient.showProduct(request);
        List<ServiceCapability> responelist = response.getServiceCapabilities();
        List<ServiceProperty> properties = responelist.get(0).getProperties();
        //从缓存获取最新数据
        String datalist = (String)redisTemplate.opsForHash()
                .get(CacheConstants.IOT_DEVICE_LAST_DATA, device.getIotId());
        if (StrUtil.isEmpty(datalist)){
            throw new BaseException("设备数据不存在");
        }
        List<DeviceData> list = JSONUtil.toList(datalist, DeviceData.class);

        List<PropertyStatusInfoVo> voList = new ArrayList<>();
        System.out.println(list);
        System.out.println(properties);
        list.forEach(deviceData -> {
            PropertyStatusInfoVo vo = new PropertyStatusInfoVo();
            properties.forEach(property -> {
                if (property.getPropertyName().equals(deviceData.getFunctionId())){
                    vo.setTime(String.valueOf(deviceData.getCreateTime().getTime()));
                    vo.setName(property.getDescription());
                    vo.setUnit(property.getUnit());
                    vo.setValue(deviceData.getDataValue());
                    vo.setDataType(property.getDataType());
                    vo.setIdentifier(property.getPropertyName());
                }
            });
            voList.add(vo);
        });

        return voList;
    }

    /**
     * 按照天查询
     * @param dto
     * @return
     */
    @Override
    public List<DayDataVo> queryDeviceDataListByWeek(String functionId, Long startTimeStr, Long endTimeStr, String iotId) {
        /*
        思路分析
        根据前端传进来的dto中的数据进行条件查询
        与查询每日逻辑不同的时,前端只需要七个参数,如何封装这七天的参数
         */
        //将毫秒值转换成时间
        LocalDateTime startTime = DateUtil.date(startTimeStr).toLocalDateTime();
        LocalDateTime endTime = DateUtil.date(endTimeStr).toLocalDateTime();
        //调用DeviceDataService层进行条件查询
        List<DeviceData> list = deviceDataService.list(Wrappers.<DeviceData>lambdaQuery()
                .eq(DeviceData::getFunctionId, functionId)
                .eq(DeviceData::getIotId, iotId)
                .between(DeviceData::getAlarmTime, startTime, endTime));
        //遍历集合封装返回参数
        //将LocalDateTime的时间格式转换成"HH:mm"
        List<DayDataVo> targetList = new ArrayList<>();
        //根据每天对数据进行分组
        Map<String,List<DeviceData>> deviceDataByWeek = list.stream().collect(Collectors.groupingBy(item->item.getAlarmTime().format(DateTimeFormatter.ofPattern("MM.dd"))));
        //获取开始时间和结束时间
        LocalDate currentDate = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();
        //循环判断当前currentDate是否超过endDate
        while (!currentDate.isAfter(endDate)){
            String day = currentDate.format(DateTimeFormatter.ofPattern("MM.dd"));
            //检查当天是否有数据
            if (deviceDataByWeek.containsKey(day)) {
                //说明当天有数据
                //遍历deviceDataList集合对数据求平均值
                List<DeviceData> deviceDataList = deviceDataByWeek.get(day);
                double averageValue = deviceDataList.stream().mapToDouble(deviceData -> NumberUtils.toDouble(deviceData.getDataValue())).average().orElse(0.0);
                //封装数据
                DayDataVo deviceDataByTimeVo = DayDataVo.builder()
                        .dateTime(day)
                        .dataValue(averageValue)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }else{
                //说明当天没有数据
                //封装数据
                DayDataVo deviceDataByTimeVo = DayDataVo.builder()
                        .dateTime(day)
                        .dataValue(null)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }
            //步增语句
            currentDate = currentDate.plusDays(1);
        }
        return targetList;
    }

}
